#ifndef APP_H
#define APP_H

#include <QMainWindow>
#include <QScrollArea>
#include <QToolBar>

#include "shape.h"
#include "widgets/label_dialog.h"
#include "widgets/label_list_widget.h"
#include "widgets/unique_label_qlist_widget.h"
#include "widgets/zoom_widget.h"
#include "widgets/canvas.h"
#include "configuration.h"


class QWidgetAction;

enum { FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM };


class app : public QMainWindow
{
	Q_OBJECT

public:
	app(QWidget* parent = nullptr);
	~app();
	bool noShapes() const;

private:
	const QString app_name_ = "labelmex";
	configuration config_;
	// Whether we need to save or not.
	bool dirty_ = false;
	bool _noSelectionSlot_ = false;
	std::vector<Shape*> _copied_shapes_;
	LabelDialog* label_dialog_;
	LabelListWidget* label_list_;
	QString lastOpenDir_;
	QDockWidget* flag_dock_;
	QListWidget* flag_widget_;
	QDockWidget* shape_dock_;
	UniqueLabelQListWidget* uniqLabelList_;
	QDockWidget* label_dock_;
	QLineEdit* fileSearch_;
	QListWidget* fileListWidget_;
	QVBoxLayout* fileListLayout_;
	QDockWidget* file_dock_;
	QWidget* fileWidget_;
	ZoomWidget* zoomWidget_;
	Canvas* canvas_;
	QScrollArea* scrollArea_;

	QMap<Qt::Orientation, QScrollBar*> scrollBars_;

	QAction* quit_action_ = nullptr;
	QAction* open_action_ = nullptr;
	QAction* opendir_action_ = nullptr;
	QAction* openNextImg_action_ = nullptr;
	QAction* openPrevImg_action_ = nullptr;
	QAction* save_action_ = nullptr;
	QAction* saveAs_action_ = nullptr;
	QAction* deleteFile_action_ = nullptr;
	QAction* changeOutputDir_action_ = nullptr;
	QAction* saveAuto_action_ = nullptr;
	QAction* saveWithImageData_action_ = nullptr;
	QAction* close_action_ = nullptr;
	QAction* toggleKeepPrevMode_action_ = nullptr;
	QAction* createMode_action_ = nullptr;
	QAction* createRectangleMode_action_ = nullptr;
	QAction* createCircleMode_action_ = nullptr;
	QAction* createLineMode_action_ = nullptr;
	QAction* createPointMode_action_ = nullptr;
	QAction* createLineStripMode_action_ = nullptr;
	QAction* createAiPolygonMode_action_ = nullptr;
	QAction* createAiMaskMode_action_ = nullptr;
	QAction* editMode_action_ = nullptr;
	QAction* delete_action_ = nullptr;
	QAction* duplicate_action_ = nullptr;
	QAction* copy_action_ = nullptr;
	QAction* paste_action_ = nullptr;
	QAction* undoLastPoint_action_ = nullptr;
	QAction* removePoint_action_ = nullptr;
	QAction* undo_action_ = nullptr;
	QAction* hideAll_action_ = nullptr;
	QAction* showAll_action_ = nullptr;
	QAction* toggleAll_action_ = nullptr;
	QAction* help_action_ = nullptr;

	QWidgetAction* zoom_widget_action_ = nullptr;
	QAction* zoomIn_action_ = nullptr;
	QAction* zoomOut_action_ = nullptr;
	QAction* zoomOrg_action_ = nullptr;
	QAction* keepPrevScale_action_ = nullptr;
	QAction* fitWindow_action_ = nullptr;
	QAction* fitWidth_action_ = nullptr;
	QAction* brightnessContrast_action_ = nullptr;

	int zoomMode_ = FIT_WINDOW;

	QMap<int, std::function<double()>> scalers_;

	QAction* edit_action_ = nullptr;
	QAction* fillDrawing_action_ = nullptr;

	QMenu* labelMenu_ = nullptr;

	QMenu* fileMenu_ = nullptr;
	QMenu* editMenu_ = nullptr;
	QMenu* viewMenu_ = nullptr;
	QMenu* helpMenu_ = nullptr;
	QMenu* recentFiles_menu_ = nullptr;
	QComboBox* selectAiModelComboBox_ = nullptr;
	QWidgetAction* selectAiModel = nullptr;

	QToolBar* tools_;

	QString output_file_;
	QString output_dir_;

	QImage image_;
	QString imagePath_;
	QStringList recentFiles_;
	const int maxRecent_ = 7;
	QVariant otherData_;
	int zoom_level_ = 100;
	bool fit_window_ = false;
	std::map<QString, std::pair<int, double>> zoom_values_;
	std::map<QString, std::pair<int, int>> brightnessContrast_values_;
	std::map<Qt::Orientation, std::map<QString, int>> scroll_values_ = {
		{Qt::Horizontal, {}},
		{Qt::Vertical, {}}
	};

	QString filename_;

	double scaleFitWindow();
	double scaleFitWidth();

	void importDirImages(QString filename, bool flag);
	void queueEvent(const std::function<void()>& function);
	void status(const QString& message, int delay);
	void _submit_ai_prompt();
	void resetState();
	LabelListWidgetItem* currentItem();
	void addRecentFile(const QString& filename);
	void loadFile(QString filename);
	void populateModeActions();
	void saveLabels(QString label_file);
	void setDirty();
	void setClean();
	void toggleActions(bool value);
	bool hasLabelFile();
	void loadShapes(const QList<Shape*>& shapes);
	void loadRecent(QString filename);

private slots:
	void openFile();
	void openDirDialog();
	void openNextImg();
	void openPrevImg();
	void saveFile();
	void saveFileAs();
	void deleteFile();
	void changeOutputDirDialog();
	void saveAuto(bool checked);
	void enableSaveImageWithData();
	void closeFile();
	void toggleKeepPrevMode();
	void toggleDrawMode(bool edit = true, const QString& createMode = "polygon");
	void setEditMode();
	void popLabelListMenu(const QPoint& point);
	bool validateLabel(const QString& label);
	void deleteSelectedShape();
	void duplicateSelectedShape();
	void copySelectedShape();
	void pasteSelectedShape();
	void removeSelectedPoint();
	void undoShapeEdit();
	void togglePolygons(bool value);
	void togglePolygons();
	void tutorial();
	void toggleDrawingSensitive(bool drawing = true);
	void addZoom(double value);
	void setZoom(double value);
	void enableKeepPrevScale(bool checked);
	void setFitWindow(bool value);
	void setFitWidth(bool value);
	void brightnessContrast();
	void _edit_label();
	void updateFileMenu();
	void copyShape();
	void moveShape();
	void paintCanvas();
};
#endif // APP_H
